/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.tencent.tendon.convert.jsontest;

import com.tencent.tendon.convert.util.AnyArray;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;

/**
 *
 * @author nbzhang
 */
@SuppressWarnings("unchecked")
public class Utility {

    private static final DateFormat fulldf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static String now() {
        synchronized (fulldf) {
            return fulldf.format(new java.util.Date());
        }
    }

    public static <T> T createRecord(final Class<T> clazz) {
        T record = null;
        try {
            record = clazz.newInstance();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        boolean[] bools = new boolean[20];
        Arrays.fill(bools, true);
        byte[] bytes = new byte[20];
        Arrays.fill(bytes, (byte) 100);
        short[] shorts = new short[20];
        Arrays.fill(shorts, (short) 100);
        char[] chars = new char[20];
        Arrays.fill(chars, '我');
        int[] ints = new int[20];
        Arrays.fill(ints, 100);
        float[] floats = new float[20];
        Arrays.fill(floats, 100.f);
        long[] longs = new long[20];
        Arrays.fill(longs, 100L);
        double[] doubles = new double[20];
        Arrays.fill(doubles, 100.0);


        Boolean[] bool2s = new Boolean[20];
        Arrays.fill(bool2s, Boolean.TRUE);
        Byte[] byte2s = new Byte[20];
        Arrays.fill(byte2s, (byte) 100);
        Short[] short2s = new Short[20];
        Arrays.fill(short2s, (short) 100);
        Character[] char2s = new Character[20];
        Arrays.fill(char2s, '我');
        Integer[] int2s = new Integer[20];
        Arrays.fill(int2s, 100);
        Float[] float2s = new Float[20];
        Arrays.fill(float2s, 100.f);
        Long[] long2s = new Long[20];
        Arrays.fill(long2s, 100L);
        Double[] double2s = new Double[20];
        Arrays.fill(double2s, 100.0);

        String[] strs = new String[20];
        for (int i = 0; i < strs.length; i++) {
            strs[i] = "字符串(]'\":=_" + (i + 1);
        }

        int index = 10;
        try {
            for (Field field : record.getClass().getDeclaredFields()) {
                if (Modifier.isStatic(field.getModifiers())) continue;
                if (Modifier.isFinal(field.getModifiers())) continue;
                ++index;
                field.setAccessible(true);
                if (field.getType() == boolean.class || field.getType() == Boolean.class) {
                    field.set(record, true);
                } else if (field.getType() == byte.class || field.getType() == Byte.class) {
                    field.set(record, Byte.MAX_VALUE);
                } else if (field.getType() == short.class || field.getType() == Short.class) {
                    field.set(record, Short.MAX_VALUE);
                } else if (field.getType() == char.class || field.getType() == Character.class) {
                    field.set(record, '我');
                } else if (field.getType() == int.class || field.getType() == Integer.class) {
                    field.set(record, 1234567890 + index);
                } else if (field.getType() == float.class || field.getType() == Float.class) {
                    field.set(record, 1234567890.0f + index);
                } else if (field.getType() == long.class || field.getType() == Long.class) {
                    field.set(record, System.currentTimeMillis() + index);
                } else if (field.getType() == double.class || field.getType() == Double.class) {
                    field.set(record, System.currentTimeMillis() + 0.0 + index);
                } else if (field.getType() == String.class) {
                    field.set(record, "字符串()[]'\":=_" + index);
                } else if (field.getType() == boolean[].class) {
                    field.set(record, bools);
                } else if (field.getType() == byte[].class) {
                    field.set(record, bytes);
                } else if (field.getType() == short[].class) {
                    field.set(record, shorts);
                } else if (field.getType() == char[].class) {
                    field.set(record, chars);
                } else if (field.getType() == int[].class) {
                    field.set(record, ints);
                } else if (field.getType() == float[].class) {
                    field.set(record, floats);
                } else if (field.getType() == long[].class) {
                    field.set(record, longs);
                } else if (field.getType() == double[].class) {
                    field.set(record, doubles);
                } else if (field.getType() == Boolean[].class) {
                    field.set(record, bool2s);
                } else if (field.getType() == Byte[].class) {
                    field.set(record, byte2s);
                } else if (field.getType() == Short[].class) {
                    field.set(record, short2s);
                } else if (field.getType() == Character[].class) {
                    field.set(record, char2s);
                } else if (field.getType() == Integer[].class) {
                    field.set(record, int2s);
                } else if (field.getType() == Float[].class) {
                    field.set(record, float2s);
                } else if (field.getType() == Long[].class) {
                    field.set(record, long2s);
                } else if (field.getType() == Double[].class) {
                    field.set(record, double2s);
                } else if (field.getType() == String[].class) {
                    field.set(record, strs);
                } else if (!field.getType().getName().startsWith("java")) {
                    field.set(record, createRecord(field.getType()));
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return record;
    }

    private static ThreadLocal<AnyArray> threadlocal = new ThreadLocal() {
        @Override
        protected AnyArray initialValue() {
            return new AnyArray();
        }
    };

    private static String deep(AnyArray refer, Object data) {
        int index = refer.indexOf(data);
        if (index > -1) {
            int deep = refer.size() - (index);
            String tv = "{\"@\":\"";
            for (int i = 0; i < deep; i++) {
                tv += '@';
            }
            return tv + "\"}";
        }
        return null;
    }

    public static <T> String listingString(T data) {
        if (data == null) return "null";
        StringBuilder sb = new StringBuilder(100);
        sb.append("{");
        Class<?> clazz = data.getClass();
        AnyArray refer = threadlocal.get();
        if (!clazz.isArray() && !Collection.class.isAssignableFrom(clazz) && !Map.class.isAssignableFrom(clazz)) {
            String r = deep(refer, data);
            if (r != null) return r;
        }
        refer.add(data);
        if (data.getClass().isArray()) {
            Object[] ar = (Object[]) data;
            StringBuilder b = new StringBuilder();
            b.append('[');
            boolean f = true;
            for (Object o : ar) {
                if (!f) b.append(',');
                if (o instanceof String) {
                    b.append('"').append(o).append('"');
                } else {
                    b.append(o);
                }
                if (f) f = false;
            }
            refer.poll();
            b.append(']');
            return b.toString();
        }
        if (Collection.class.isAssignableFrom(clazz)) {
            Collection a = (Collection) data;
            StringBuilder b = new StringBuilder();
            b.append('[');
            boolean f = true;
            for (Object o : a) {
                if (!f) b.append(',');
                if (o instanceof String) {
                    b.append('"').append(o).append('"');
                } else {
                    b.append(o);
                }
                if (f) f = false;
            }
            refer.poll();
            return b.append("]").toString();
        }
        if (Map.class.isAssignableFrom(clazz)) {
            Map<?, ?> a = (Map) data;
            StringBuilder b = new StringBuilder();
            b.append('{');
            boolean f = true;
            for (Map.Entry o : a.entrySet()) {
                if (!f) b.append(',');
                b.append('"').append(o.getKey()).append("\":").append(o.getValue());
                if (f) f = false;
            }
            refer.poll();
            return b.append("}").toString();
        }
        if (clazz.getName().startsWith("java.") || clazz.getName().startsWith("java.x")) {
            refer.poll();
            return String.valueOf(data);
        }
        try {
            boolean flag = false;
            boolean isstring = true;
            Object obj = null;
            String str = "";

            do {
                java.lang.reflect.Method[] methods = clazz.getDeclaredMethods();
                Arrays.sort(methods, new java.util.Comparator<java.lang.reflect.Method>() {
                    @Override
                    public int compare(Method o1, Method o2) {
                        String name1 = o1.getName();
                        name1 = name1.substring(name1.startsWith("get") ? 3 : 2);
                        String name2 = o2.getName();
                        name2 = name2.substring(name2.startsWith("get") ? 3 : 2);
                        return name1.compareTo(name2);
                    }
                });
                for (java.lang.reflect.Method m : methods) {
                    if ((m.getName().startsWith("get") || m.getName().startsWith("is")) && m.getParameterTypes().length == 0) {
                        int l = m.getName().startsWith("get") ? 3 : 2;
                        obj = m.invoke(data);
                        if (obj == null) continue;
                        isstring = obj instanceof String || obj instanceof Enum;
                        if (obj.getClass() == char.class || obj.getClass() == Character.class) obj = (int) (Character) obj;
                        if (!isstring) {
                            if (obj instanceof Number && ((Number) obj).intValue() == 0) continue;
                            if (obj instanceof Boolean && ((Boolean) obj) == false) continue;
                            if (obj.getClass().isArray()) {
                                Class<?> type = obj.getClass().getComponentType();
                                if (type == boolean.class) {
                                    obj = Arrays.toString((boolean[]) obj).replace(" ", "");
                                } else if (type == byte.class) {
                                    obj = Arrays.toString((byte[]) obj).replace(" ", "");
                                } else if (type == char.class) {
                                    char[] chs = (char[]) obj;
                                    int[] obj2 = new int[chs.length];
                                    for (int i = 0; i < chs.length; i++) {
                                        obj2[i] = chs[i];
                                    }
                                    obj = Arrays.toString(obj2).replace(" ", "");
                                } else if (type == short.class) {
                                    obj = Arrays.toString((short[]) obj).replace(" ", "");
                                } else if (type == int.class) {
                                    obj = Arrays.toString((int[]) obj).replace(" ", "");
                                } else if (type == long.class) {
                                    obj = Arrays.toString((long[]) obj).replace(" ", "");
                                } else if (type == float.class) {
                                    obj = Arrays.toString((float[]) obj).replace(" ", "");
                                } else if (type == double.class) {
                                    obj = Arrays.toString((double[]) obj).replace(" ", "");
                                }
                            }
                        }

                        str = isstring ? ("\"" + obj.toString().replace("\"", "\\\"") + "\"") : listingString(obj);
                        if (flag) sb.append(",");
                        sb.append("\"").append(m.getName().substring(l).toLowerCase()).append("\":").append(str);
                        flag = true;
                    }
                }
            } while ((clazz = clazz.getSuperclass()) != Object.class);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        refer.poll();
        sb.append("}");
        return sb.toString();
    }
}
